import { Component } from 'react'
import { Button, Progress, notification, Table, InputNumber } from 'antd'
import request from '../utils/request'
import { WAITING, UPLOADING, PAUSE, columns } from '../utils/constant'

const MB = 1000 * 1000
let SIZE = 1 * MB
export default class Upload extends Component {
  state = {
    totalUploadPercentage: 0,
    container: {
      file: null,
      hash: '', // 哈希
      worker: null,
    },
    status: WAITING,
    hashPercentage: 0,
    shouldUploadChunk: [], //上传的切片数据
    alreadyUploadChunk: [], // 已上传的切片数据
    xhrList: [], // xhr请求数组
    fileChunkList: [], // 文件块
  }

  // 选择文件
  handleFileChange = (e) => {
    const [file] = e.target.files // 拿到第一个文件
    this.setState({
      totalUploadPercentage: 0,
      container: {
        file,
        hash: '',
        worker: null,
      },
      hashPercentage: 0,
      shouldUploadChunk: [], //上传的切片数据
      alreadyUploadChunk: [], // 已上传的切片数据
      xhrList: [], // xhr请求数组
      fileChunkList: [], // 文件块
    })
    // 初始化
    this.resetData()
    console.log('要上传的文件：', file)
  }

  // 点击上传
  handleUpload = async () => {
    const { container } = this.state
    if (!container.file) return
    this.setState({
      status: UPLOADING,
    })
    // 文件切片
    const fileChunkList = this.createFileChunk(container.file)
    // console.log("文件切片：", fileChunkList);
    this.setState({
      fileChunkList,
    })
    // 计算文件hash值，并拿到进度条
    let hash = await this.calculateHash(fileChunkList)
    this.setState({
      container: {
        ...container,
        hash,
      },
    })
    // 处理文件切片
    await this.handleChunkList(fileChunkList)
    // 上传切片
    await this.uploadChunks()
  }

  // 将文件分片
  createFileChunk = (file, size = SIZE) => {
    console.log(size, '切片大小')
    const fileChunkList = []
    let cur = 0
    while (cur < file.size) {
      fileChunkList.push({
        chunk: file.slice(cur, cur + size),
      })
      cur += size
      console.log(cur, 11)
    }
    return fileChunkList
  }

  // 计算文件hash 防止线程阻塞
  calculateHash = async (fileChunkList) =>
    new Promise((resolve) => {
      notification.info({ message: '计算文件hash值中！' })
      // web worker
      const { container } = this.state
      let worker = new Worker('/worker/hash.js')
      this.setState({
        container: {
          ...container,
          worker,
        },
      })
      worker.postMessage({ fileChunkList })
      worker.onmessage = (e) => {
        const { percentage, hash } = e.data
        console.log('文件hash计算进度：', percentage)
        this.setState({
          hashPercentage: percentage,
        })
        if (hash) {
          resolve(hash)
        }
      }
    })

  // 处理上传文件块列表
  handleChunkList = async (fileChunkList) => {
    const { container } = this.state
    // 校验服务端是否拥有文件
    let { shouldUpload, uploadedList } = await this.verifyUpload(
      container.file.name,
      container.hash
    )
    if (!shouldUpload) {
      notification.success({
        message: '服务器已存在文件,不需要重复上传.',
      })
      this.setState({
        status: WAITING,
      })
      return
    }
    let shouldUploadChunk = [],
      alreadyUploadChunk = []
    fileChunkList.forEach(({ chunk }, index) => {
      let obj = {
        fileHash: container.hash,
        index,
        // 文件块命名 1ff19b68d6a41ee5be90eecf7d713de7-0 ...
        chunkHash: `${container.hash}-${index}`,
        chunk: chunk,
        size: chunk.size,
      }
      if (!uploadedList.includes(`${container.hash}-${index}`)) {
        // 未上传的切片
        shouldUploadChunk.push({
          ...obj,
          percentage: 0, //当前切片的进度
        })
      } else {
        alreadyUploadChunk.push({
          ...obj,
          percentage: 100,
        })
      }
    })
    console.log("'应该上传的切片:'", shouldUploadChunk)
    console.log("'已上传的切片:'", alreadyUploadChunk)
    this.setState({ shouldUploadChunk, alreadyUploadChunk })
  }
  // 校验服务端是否拥有文件
  verifyUpload = async (fileName, hash) => {
    const { data } = await request({
      url: 'http://localhost:4567/verify',
      headers: {
        'content-type': 'application/json',
      },
      data: JSON.stringify({
        fileName,
        hash,
      }),
    })
    return JSON.parse(data)
  }

  // 上传切片
  uploadChunks = async () => {
    const { shouldUploadChunk, container, xhrList } = this.state
    // console.log("xhrList", xhrList);
    if (shouldUploadChunk.length === 0) {
      // 全部上传完, 设置total进度条
      this.setState({
        totalUploadPercentage: 100,
      })
    } else {
      const requestList = shouldUploadChunk
        .map(({ chunk, chunkHash, index }) => {
          const formData = new FormData()
          formData.append('chunk', chunk)
          formData.append('chunkHash', chunkHash)
          formData.append('fileName', container.file.name)
          formData.append('fileHash', container.hash)
          return { formData, index }
        })
        .map(({ formData, index }) =>
          request({
            url: 'http://localhost:4567',
            data: formData,
            onProgress: this.createProgressHandler(index),
            xhrList,
          })
        )
      await Promise.all(requestList)
    }
    // console.log(requestList, xhrList, "发送请求的promise 和 xhr");
    // console.log(shouldUploadChunk, container, "应该上传的文件块和文件");
    // console.log("文件块promise请求结果：", data);
    // 上传结束
    // console.log("发送合并文件请求");
    await this.mergeRequest()
    notification.success({ message: '上传成功！' })
  }

  // 文件块进度条
  createProgressHandler = (index) => (e) => {
    const shouldUploadChunk = [...this.state.shouldUploadChunk]
    let percentage = parseInt(String((e.loaded / e.total) * 100))
    this.setState(
      {
        shouldUploadChunk: shouldUploadChunk.map((item) =>
          item.index === index ? { ...item, percentage } : item
        ),
      },
      // 更新完文件块的进度后 更新总进度
      () => this.uploadTotalPercentage()
    )
  }

  // 总文件进度条
  uploadTotalPercentage = () => {
    // 数据驱动视图
    const { container, shouldUploadChunk, alreadyUploadChunk } = this.state
    // 更新文件上传进度 已上传的文件进度条
    const ALloaded = alreadyUploadChunk.reduce(
      (total, acc) => acc.size + total,
      0
    )
    const loaded = shouldUploadChunk
      .map((chunk) => chunk.size * chunk.percentage)
      .reduce((acc, cur) => acc + cur)
    let totalUploadPercentage = (loaded + ALloaded * 100) / container.file.size
    this.setState({
      totalUploadPercentage,
    })
  }

  // 文件合并请求
  mergeRequest = async () => {
    const { container } = this.state
    let data = await request({
      url: 'http://localhost:4567/merge',
      headers: {
        'content-type': 'application/json',
      },
      data: JSON.stringify({
        size: SIZE,
        fileHash: container.hash,
        fileName: container.file.name,
      }),
    })
    console.log('合并结果：', data)
  }
  // 暂停上传
  handlePause = () => {
    // 暂停上传
    this.setState({
      status: PAUSE,
    })
    notification.info({ message: '已暂停上传！' })
    this.resetData()
  }

  // 初始化
  resetData = () => {
    const { xhrList } = this.state
    // 中止请求
    xhrList.forEach((xhr) => xhr.abort())
    this.setState({
      xhrList: [],
    })
  }

  // 恢复上传
  handleResume = async () => {
    const { fileChunkList } = this.state
    notification.info({ message: '恢复下载' })
    this.setState({
      status: UPLOADING,
    })
    // 处理文件切片
    await this.handleChunkList(fileChunkList)
    // 上传切片
    await this.uploadChunks()
  }

  render() {
    const {
      hashPercentage,
      shouldUploadChunk,
      alreadyUploadChunk,
      container,
      totalUploadPercentage,
    } = this.state
    return (
      <div style={{ textAlign: 'center', padding: 40 }}>
        <div>
          <input type="file" onChange={this.handleFileChange} />
          <Button onClick={this.handleUpload} type="primary">
            上传
          </Button>
          <Button style={{ margin: 20 }} onClick={this.handleResume}>
            恢复
          </Button>
          <Button type="primary" danger={true} onClick={this.handlePause}>
            暂停
          </Button>
          <span style={{ marginLeft: 20 }}>分片大小：</span>
          <InputNumber
            min={0.1}
            max={100}
            defaultValue={1}
            onChange={this.handleChangeSize}
          ></InputNumber>
          <span> MB</span>
        </div>
        <div style={{ marginTop: 20 }}>
          文件hash计算进度 : {container.hash}
          <Progress percent={hashPercentage.toFixed(2)}></Progress>
          文件上传进度
          <Progress percent={totalUploadPercentage.toFixed(2)}></Progress>
        </div>
        <Table
          dataSource={shouldUploadChunk}
          columns={columns}
          rowKey="chunkHash"
          pagination={false}
        ></Table>
        {alreadyUploadChunk.length ? (
          <>
            <h1>已在服务器的切片：{alreadyUploadChunk.length}</h1>
            <Table
              dataSource={alreadyUploadChunk}
              columns={columns}
              pagination={false}
              rowKey="chunkHash"
            ></Table>
          </>
        ) : null}
      </div>
    )
  }

  handleChangeSize = (val) => {
    SIZE = Number((val * MB).toFixed(0))
    console.log(SIZE)
  }
}
